import select
from socket import *
import queue
import sys

server = socket(AF_INET, SOCK_STREAM)

server.setblocking(False)

server.bind(('0.0.0.0',8080))

server.listen(5)

# 所有连接进来的对象都放在inputs
inputs = [server, ] #server用于监控是否有些的客户端连接

# 需要发送数据的对象
outputs = []

# 对外发送数据的队列,记录到字典中
message_queues = {}

while True:
  readable, writable, exceptional = select.select(inputs, outputs, inputs)

  for s in readable:

    if s is server: # 判断是否有新客户端连接
      clientSocket, client_addr = s.accept()
      print('new connection from', client_addr)

      # 设置客户端socket为非阻塞
      clientSocket.setblocking(False)
      # 为了不阻塞整个程序,我们不会立刻在这里开始接收客户端发来的数据, 把它放到inputs里, 下一次loop时,这个新连接
      # 就会被交给select去监听,如果这个连接的客户端发来了数据 ,那这个连接的fd在server端就会变成就续的,select就会把这个连接返回,
      # 返回到readable 列表里,然后你就可以loop readable列表,取出这个连接,开始接收数据了, 下面就是这么干的
      inputs.append(clientSocket)
      # 接收到客户端的数据后,不立刻返回,暂存在队列里, 以后发送
      message_queues[clientSocket] = queue.Queue()
    
    else:
      data = s.recv(1024) # 表示已连接的客户端发送过来的消息

      if data:
        print('received [%s] from %s' % (data, s.getpeername()[0]))
        message_queues[s].put(data) # 接收到的数据先放到queue里,一会返回给客户端

        if s not in outputs:
          outputs.append(s) # 为了不影响处理与其他客户端的连接,这里不立刻返回数据给客户端

      else: # 代表客户端已经关闭
        print("client [%s] closed", s)
        if s in outputs:
          outputs.remove(s)
        inputs.remove(s)
        # 关闭的连接在队列中也删除
        del message_queues[s]


  for s in writable:
    try:
      next_msg = message_queues[s].get_nowait() # get_nowait() 方法非阻塞,当没有数据返回时将抛出异常
    except queue.Empty:
      print('output queue for [%s] is empty'%s.getpeername()[0])
      outputs.remove(s) # 表示没有要发送给该客户端socket的消息
    else:
      print('send %s to %s'%(next_msg, s.getpeername()[0]))
      s.send(next_msg)

  for s in exceptional:
    print('handling exceptional condition for', s.getpeername()[0])
    # 从inputs中删除
    inputs.remove(s)

    if s in outputs:
      outputs.remove(s)
    s.close()

    # 删除队列
    del message_queues[s]

